package cn.iocoder.yudao.module.vlog.statistics.service;

import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.business.basic.pojo.bo.DataComparison;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.module.api.shop.info.ShopInfoApi;
import cn.iocoder.yudao.module.vlog.statistics.dal.dataobject.VLogStatisticsDO;
import cn.iocoder.yudao.module.vlog.statistics.dal.mysql.VLogStatisticsMapper;
import cn.iocoder.yudao.module.vlog.statistics.vo.VLogStatisticsReqVO;
import cn.iocoder.yudao.module.vlog.statistics.vo.VLogStatisticsRespVO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;

/**
 * 视频统计 Service 实现类
 *
 * @author 山野羡民（1032694760@qq.com）
 */
@Service
@Validated
@Slf4j
public class VLogStatisticsServiceImpl implements VLogStatisticsService {

    @Resource
    private VLogStatisticsMapper vLogStatisticsMapper;
    @Resource
    private ShopInfoApi shopInfoApi;

    @Override
    public void increaseVLogCount(Long userId) {
        doIncreaseCount(userId, (statisticsDO, isInsert) -> {
            statisticsDO.setVlogCount(isInsert ? 1 : statisticsDO.getVlogCount() + 1);
            return null;
        });
    }

    @Override
    public void decreaseVLogCount(Long userId) {
        doDecreaseCount(userId, (statisticsDO) -> statisticsDO.setVlogCount(Math.max(statisticsDO.getVlogCount() - 1, 0)));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void increaseLikeCount(Long fromUserId, Long toUserId) {
        doIncreaseCount(fromUserId, (statisticsDO, isInsert) -> {
            statisticsDO.setReceiveLikeCount(isInsert ? 1 : statisticsDO.getSendLikeCount() + 1);
            return null;
        });
        doIncreaseCount(toUserId, (statisticsDO, isInsert) -> {
            statisticsDO.setSendLikeCount(isInsert ? 1 : statisticsDO.getReceiveLikeCount() + 1);
            return null;
        });
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void decreaseLikeCount(Long fromUserId, Long toUserId) {
        doDecreaseCount(fromUserId, (statisticsDO) -> statisticsDO.setSendLikeCount(Math.max(statisticsDO.getSendLikeCount() - 1, 0)));
        doDecreaseCount(toUserId, (statisticsDO) -> statisticsDO.setReceiveLikeCount(Math.max(statisticsDO.getReceiveLikeCount() - 1, 0)));
    }

    @Override
    public void increaseFavoriteCount(Long fromUserId, Long toUserId) {
        doIncreaseCount(fromUserId, (statisticsDO, isInsert) -> {
            statisticsDO.setSendFavoriteCount(isInsert ? 1 : statisticsDO.getSendFavoriteCount() + 1);
            return null;
        });
        doIncreaseCount(toUserId, (statisticsDO, isInsert) -> {
            statisticsDO.setReceiveFavoriteCount(isInsert ? 1 : statisticsDO.getReceiveFavoriteCount() + 1);
            return null;
        });
    }

    @Override
    public void decreaseFavoriteCount(Long fromUserId, Long toUserId) {
        doDecreaseCount(fromUserId, (statisticsDO) -> statisticsDO.setSendFavoriteCount(Math.max(statisticsDO.getSendFavoriteCount() - 1, 0)));
        doDecreaseCount(toUserId, (statisticsDO) -> statisticsDO.setReceiveFavoriteCount(Math.max(statisticsDO.getReceiveFavoriteCount() - 1, 0)));
    }

    @Override
    public void increaseFansCount(Long fromUserId, Long toUserId) {
        doIncreaseCount(fromUserId, (statisticsDO, isInsert) -> {
            statisticsDO.setFollowCount(isInsert ? 1 : statisticsDO.getFollowCount() + 1);
            return null;
        });
        doIncreaseCount(toUserId, (statisticsDO, isInsert) -> {
            statisticsDO.setFansCount(isInsert ? 1 : statisticsDO.getFansCount() + 1);
            return null;
        });
    }

    @Override
    public void decreaseFansCount(Long fromUserId, Long toUserId) {
        doDecreaseCount(fromUserId, (statisticsDO) -> statisticsDO.setFollowCount(Math.max(statisticsDO.getFollowCount() - 1, 0)));
        doDecreaseCount(toUserId, (statisticsDO) -> statisticsDO.setFansCount(Math.max(statisticsDO.getFansCount() - 1, 0)));
    }

    @Override
    public VLogStatisticsDO getVLogStatistics(Long userId) {
        VLogStatisticsDO aDo = vLogStatisticsMapper.selectOne(VLogStatisticsDO::getUserId, userId);
        if (aDo == null) {
            long shopId = shopInfoApi.getShopIdByMemberUserId(userId);
            VLogStatisticsDO statisticsDO = new VLogStatisticsDO();
            statisticsDO.setUserId(userId);
            statisticsDO.setShopId(shopId);
            statisticsDO.setVlogCount(0);
            statisticsDO.setFansCount(0);
            statisticsDO.setFollowCount(0);
            statisticsDO.setReceiveLikeCount(0);
            statisticsDO.setSendLikeCount(0);
            statisticsDO.setReceiveFavoriteCount(0);
            statisticsDO.setSendFavoriteCount(0);
            statisticsDO.setReceiveShareCount(0);
            statisticsDO.setSendShareCount(0);
            return statisticsDO;
        }
        return aDo;
    }

    @Override
    public PageResult<VLogStatisticsDO> getVLogStatisticsRankPage(VLogStatisticsReqVO reqVO, SortablePageParam pageParam) {
        PageUtils.buildDefaultSortingField(pageParam, VLogStatisticsDO::getFansCount); // 默认粉丝数倒序
        return vLogStatisticsMapper.selectPageGroupByUserId(reqVO, pageParam);
    }

    @Override
    public List<VLogStatisticsDO> getVLogStatisticsList(VLogStatisticsReqVO reqVO) {
        return vLogStatisticsMapper.selectListByTimeBetween(reqVO);
    }

    @Override
    public DataComparison<VLogStatisticsRespVO> getVLogStatisticsAnalyse(VLogStatisticsReqVO reqVO) {
        LocalDateTime beginTime = ArrayUtil.get(reqVO.getTimes(), 0);
        LocalDateTime endTime = ArrayUtil.get(reqVO.getTimes(), 1);
        // 统计数据
        VLogStatisticsRespVO value = vLogStatisticsMapper.selectVoByTimeBetween(reqVO);
        // 对照数据
        LocalDateTime referenceBeginTime = beginTime.minus(Duration.between(beginTime, endTime));
        VLogStatisticsReqVO referenceReqVO = new VLogStatisticsReqVO(new LocalDateTime[]{referenceBeginTime, beginTime});
        VLogStatisticsRespVO reference = vLogStatisticsMapper.selectVoByTimeBetween(referenceReqVO);
        return new DataComparison<>(value, reference);
    }

    private void doIncreaseCount(Long userId, BiFunction<VLogStatisticsDO, Boolean, Void> function) {
        VLogStatisticsDO statisticsDO = vLogStatisticsMapper.selectOne(VLogStatisticsDO::getUserId, userId);
        if (statisticsDO == null) {
            long shopId = shopInfoApi.getShopIdByMemberUserId(userId);
            statisticsDO = new VLogStatisticsDO();
            statisticsDO.setUserId(userId);
            statisticsDO.setShopId(shopId);
            statisticsDO.setTime(LocalDate.now());
            function.apply(statisticsDO, true);
            vLogStatisticsMapper.insert(statisticsDO);
        } else {
            statisticsDO.setTime(LocalDate.now());
            function.apply(statisticsDO, false);
            vLogStatisticsMapper.updateById(statisticsDO);
        }
    }

    private void doDecreaseCount(Long userId, Consumer<VLogStatisticsDO> function) {
        VLogStatisticsDO statisticsDO = vLogStatisticsMapper.selectOne(VLogStatisticsDO::getUserId, userId);
        if (statisticsDO == null) {
            log.warn("[doDecreaseCount][userId({}) 减少计数失败，因为统计记录不存在]", userId);
        } else {
            function.accept(statisticsDO);
            vLogStatisticsMapper.updateById(statisticsDO);
        }
    }

}
